package cn.org.osgi.xwork.config.providers;
/*
 * Copyright (c) 2002-2006 by OpenSymphony
 * All rights reserved.
 */
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionDelta;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryChangeEvent;
import org.eclipse.core.runtime.IRegistryChangeListener;
import org.osgi.framework.ServiceReference;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import cn.org.osgi.module.webwork.WebworkActivator;

import com.opensymphony.util.ClassLoaderUtil;
import com.opensymphony.util.FileManager;
import com.opensymphony.util.TextUtils;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionSupport;
import com.opensymphony.xwork.ObjectFactory;
import com.opensymphony.xwork.XworkException;
import com.opensymphony.xwork.config.Configuration;
import com.opensymphony.xwork.config.ConfigurationException;
import com.opensymphony.xwork.config.ConfigurationProvider;
import com.opensymphony.xwork.config.ConfigurationUtil;
import com.opensymphony.xwork.config.ExternalReferenceResolver;
import com.opensymphony.xwork.config.entities.ActionConfig;
import com.opensymphony.xwork.config.entities.ExceptionMappingConfig;
import com.opensymphony.xwork.config.entities.ExternalReference;
import com.opensymphony.xwork.config.entities.InterceptorConfig;
import com.opensymphony.xwork.config.entities.InterceptorStackConfig;
import com.opensymphony.xwork.config.entities.PackageConfig;
import com.opensymphony.xwork.config.entities.ResultConfig;
import com.opensymphony.xwork.config.entities.ResultTypeConfig;
import com.opensymphony.xwork.config.providers.InterceptorBuilder;
import com.opensymphony.xwork.config.providers.XmlHelper;
import com.opensymphony.xwork.util.DomHelper;
import com.opensymphony.xwork.util.location.Location;


/**
 * Looks in the classpath for "xwork.xml" and uses it for the XWork configuration.
 *
 * @author tmjee
 * @author Rainer Hermanns
 * @author Neo
 * @version $Revision: 1539 $
 */
public class XmlExtensionConfigurationProvider implements ConfigurationProvider,IRegistryChangeListener {

    private static final Log LOG = LogFactory.getLog(XmlExtensionConfigurationProvider.class);

    private static final String NAMESPACE="WebworkModule";
    private static final String EXTENSION_POINT="ActionExtension";
    private Configuration configuration;
    private Set includedFileNames = new TreeSet();
    private String configFileName = "xwork.xml";
    
    private Map configFilePackages=new HashMap();

    public XmlExtensionConfigurationProvider() {
    }

    public XmlExtensionConfigurationProvider(String filename) {
    	this.configFileName = filename;
    }


    public void destroy() {
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }

        if (!(o instanceof XmlExtensionConfigurationProvider)) {
            return false;
        }

        final XmlExtensionConfigurationProvider xmlConfigurationProvider = (XmlExtensionConfigurationProvider) o;

        if ((configFileName != null) ? (!configFileName.equals(xmlConfigurationProvider.configFileName)) : (xmlConfigurationProvider.configFileName != null))
        {
            return false;
        }

        return true;
    }

    public int hashCode() {
        return ((configFileName != null) ? configFileName.hashCode() : 0);
    }

    public void init(Configuration configuration) {
        this.configuration = configuration;
        includedFileNames.clear();


        try {
            loadConfigurationFile(configFileName, null);
        } catch (ConfigurationException e) {
            throw e;
        } catch (Exception e) {
            LOG.fatal("Could not load XWork configuration file, failing", e);
            throw new ConfigurationException("Error loading configuration file " + configFileName, e);
        }
        initExtensions(); 
    }

    /**
     * Tells whether the ConfigurationProvider should reload its configuration. This method should only be called
     * if ConfigurationManager.isReloadingConfigs() is true.
     *
     * @return true if the file has been changed since the last time we read it
     */
    public boolean needsReload() {
        boolean needsReload = FileManager.fileNeedsReloading(configFileName);
        Iterator fileNameIterator = includedFileNames.iterator();

        while (!needsReload && (fileNameIterator.hasNext())) {
            String fileName = (String) fileNameIterator.next();
            needsReload = FileManager.fileNeedsReloading(fileName);
        }

        return needsReload;
    }
    
    /*
     * (non-Javadoc)
     * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent)
     */
    public void registryChanged(IRegistryChangeEvent event) {
    	IExtensionDelta[] deltas=event.getExtensionDeltas(NAMESPACE,EXTENSION_POINT);
    	for (int i = 0; i < deltas.length; i++) {
    		IConfigurationElement[] elements=deltas[i].getExtension().getConfigurationElements();
    		switch (deltas[i].getKind()) {
			case IExtensionDelta.ADDED:
				for (int j = 0; j < elements.length; j++) {
					loadConfigurationFile(elements[i].getAttribute("configFile"), null);
				}
				break;
			case IExtensionDelta.REMOVED:
				for (int j = 0; j < elements.length; j++) {
					includedFileNames.remove(elements[j]);
					String fileName=elements[j].getAttribute("configFile");
					if(configFilePackages.containsKey(fileName)){
						List packages=(List) configFilePackages.get(fileName);
						for (Iterator iter = packages.iterator(); iter
								.hasNext();) {
							String packageName = (String) iter.next();
							configuration.removePackageConfig(packageName);
						}
					}
				}
				break;
			default:
				break;
			}
		}
	}

    protected InputStream getInputStream(String fileName) {
        return FileManager.loadFile(fileName, this.getClass());
    }

    protected void addAction(Element actionElement, PackageConfig packageContext) throws ConfigurationException {
        String name = actionElement.getAttribute("name");
        String className = actionElement.getAttribute("class");
        String methodName = actionElement.getAttribute("method");
        Location location = DomHelper.getLocationObject(actionElement);

        //methodName should be null if it's not set
        methodName = (methodName.trim().length() > 0) ? methodName.trim() : null;

        // if you don't specify a class on <action/>, it defaults to ActionSupport
        if (!TextUtils.stringSet(className)) {
            className = ActionSupport.class.getName();
        }

        if (!verifyAction(className, name, location)) {
            return;
        }

        Map actionParams = XmlHelper.getParams(actionElement);

        Map results;

        try {
            results = buildResults(actionElement, packageContext);
        } catch (ConfigurationException e) {
            throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement);
        }

        List interceptorList = buildInterceptorList(actionElement, packageContext);

        List externalrefs = buildExternalRefs(actionElement, packageContext);

        List exceptionMappings = buildExceptionMappings(actionElement, packageContext);

        ActionConfig actionConfig = new ActionConfig(methodName, className, actionParams, results, interceptorList, externalrefs, exceptionMappings,
        packageContext.getName());
        actionConfig.setLocation(location);
        packageContext.addActionConfig(name, actionConfig);

        if (LOG.isDebugEnabled()) {
            LOG.debug("Loaded " + (TextUtils.stringSet(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig);
        }
    }

    protected boolean verifyAction(String className, String name, Location loc) {
        try {
            Class clazz = ObjectFactory.getObjectFactory().getClassInstance(className);
            if (ObjectFactory.getObjectFactory().isNoArgConstructorRequired()) {
                if (!Modifier.isPublic(clazz.getModifiers())) {
                    LOG.error("Action class [" + className + "] is not public, skipping action [" + name + "]");
                    return false;
                }
                clazz.getConstructor(new Class[] {});
            }
            return true;
        } catch (ClassNotFoundException e) {
            LOG.error("Action class [" + className + "] not found, skipping action [" + name + "] at "
                + loc, e);
            return false;
        } catch (NoSuchMethodException e) {
            LOG.error("Action class [" + className + "] does not have a public no-arg constructor,"
                    + " skipping action [" + name + "] at " + loc, e);
            return false;
        } catch (Exception ex) {
            throw new ConfigurationException(ex, loc);
        }    
    }

    /**
     * Create a PackageConfig from an XML element representing it.
     */
    protected void addPackage(Element packageElement) throws ConfigurationException {
        PackageConfig newPackage = buildPackageContext(packageElement);

        if (LOG.isDebugEnabled()) {
            LOG.debug("Loaded " + newPackage);
        }

        // add result types (and default result) to this package
        addResultTypes(newPackage, packageElement);

        // load the interceptors and interceptor stacks for this package
        loadInterceptors(newPackage, packageElement);

        // load the default interceptor reference for this package
        loadDefaultInterceptorRef(newPackage, packageElement);

        // load the global result list for this package
        loadGlobalResults(newPackage, packageElement);

        // load the global exception handler list for this package
        loadGobalExceptionMappings(newPackage, packageElement);

        // get actions
        NodeList actionList = packageElement.getElementsByTagName("action");

        for (int i = 0; i < actionList.getLength(); i++) {
            Element actionElement = (Element) actionList.item(i);
            addAction(actionElement, newPackage);
        }
        
        // load the default action reference for this package
        loadDefaultActionRef(newPackage, packageElement);

        configuration.addPackageConfig(newPackage.getName(), newPackage);
    }

    protected void addResultTypes(PackageConfig packageContext, Element element) {
        NodeList resultTypeList = element.getElementsByTagName("result-type");

        for (int i = 0; i < resultTypeList.getLength(); i++) {
            Element resultTypeElement = (Element) resultTypeList.item(i);
            String name = resultTypeElement.getAttribute("name");
            String className = resultTypeElement.getAttribute("class");
            String def = resultTypeElement.getAttribute("default");
            
            Location loc = DomHelper.getLocationObject(resultTypeElement);

            Class clazz = verifyResultType(className, loc);
            if (clazz != null) {
            	String paramName = null;
            	try {
            		paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
            	}
            	catch(Throwable t) {
            		// if we get here, the result type doesn't have a default param defined.
            	}
                ResultTypeConfig resultType = new ResultTypeConfig(name, className, paramName);
                resultType.setLocation(DomHelper.getLocationObject(resultTypeElement));
                
                Map params = XmlHelper.getParams(resultTypeElement);
    
                if (!params.isEmpty()) {
                    resultType.setParams(params);
                }
                packageContext.addResultTypeConfig(resultType);
                
                // set the default result type
                if ("true".equals(def)) {
                    packageContext.setDefaultResultType(name);
                }
            }
        }
    }

    protected Class verifyResultType(String className, Location loc) {
        try {
            return ObjectFactory.getObjectFactory().getClassInstance(className);
        } catch (ClassNotFoundException e) {
            LOG.warn("Result class [" + className + "] doesn't exist (ClassNotFoundException) at " +
                loc.toString() + ", ignoring", e);
        } catch (NoClassDefFoundError e) {
            LOG.warn("Result class [" + className + "] doesn't exist (NoClassDefFoundError) at " +
                loc.toString() + ", ignoring", e);
        }

        return null;
    }

    protected List buildExternalRefs(Element element, PackageConfig context) throws ConfigurationException {
        List refs = new ArrayList();
        NodeList externalRefList = element.getElementsByTagName("external-ref");

        String refName;
        String refValue = null;
        String requiredTemp;
        boolean required;

        for (int i = 0; i < externalRefList.getLength(); i++) {
            Element refElement = (Element) externalRefList.item(i);

            if (refElement.getParentNode().equals(element) || refElement.getParentNode().getNodeName().equals(element.getNodeName())) {
                refName = refElement.getAttribute("name");

                //If the external ref is not declared explicitly, we can introspect the
                //reference type using it's name and try resolving the reference using it's class type
                if (refElement.getChildNodes().getLength() > 0) {
                    refValue = XmlHelper.getContent(refElement);
                }

                requiredTemp = refElement.getAttribute("required");

                if ((requiredTemp == null) || "".equals(requiredTemp)) {
                    required = true;
                } else {
                    required = Boolean.valueOf(requiredTemp).booleanValue();
                }

                refs.add(new ExternalReference(refName, refValue, required));
            }
        }

        return refs;
    }

    protected List buildInterceptorList(Element element, PackageConfig context) throws ConfigurationException {
        List interceptorList = new ArrayList();
        NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");

        for (int i = 0; i < interceptorRefList.getLength(); i++) {
            Element interceptorRefElement = (Element) interceptorRefList.item(i);

            if (interceptorRefElement.getParentNode().equals(element) || interceptorRefElement.getParentNode().getNodeName().equals(element.getNodeName())) {
                List interceptors = lookupInterceptorReference(context, interceptorRefElement);
                interceptorList.addAll(interceptors);
            }
        }

        return interceptorList;
    }

    /**
     * This method builds a package context by looking for the parents of this new package.
     * <p/>
     * If no parents are found, it will return a root package.
     */
    protected PackageConfig buildPackageContext(Element packageElement) {
        String parent = packageElement.getAttribute("extends");
        String abstractVal = packageElement.getAttribute("abstract");
        boolean isAbstract = Boolean.valueOf(abstractVal).booleanValue();
        String name = TextUtils.noNull(packageElement.getAttribute("name"));
        String namespace = TextUtils.noNull(packageElement.getAttribute("namespace"));

        //RM* Load the ExternalReferenceResolver if one has been set
        ExternalReferenceResolver erResolver = null;

        String externalReferenceResolver = TextUtils.noNull(packageElement.getAttribute("externalReferenceResolver"));

        PackageConfig cfg = null;
        if (!("".equals(externalReferenceResolver))) {
            try {
                erResolver = (ExternalReferenceResolver) ObjectFactory.getObjectFactory().buildBean(externalReferenceResolver, null);
            } catch (ClassNotFoundException e) {
                //TODO this should be localized
                String msg = "Could not find External Reference Resolver: " + externalReferenceResolver + ". " + e.getMessage();
                LOG.error(msg);
                throw new ConfigurationException(msg, e, packageElement);
            } catch (Exception e) {
                //TODO this should be localized
                String msg = "Could not create External Reference Resolver: " + externalReferenceResolver + ". " + e.getMessage();
                LOG.error(msg);
                throw new ConfigurationException(msg, e, packageElement);
            }
        }

        if (!TextUtils.stringSet(TextUtils.noNull(parent))) { // no parents

            cfg = new PackageConfig(name, namespace, isAbstract, erResolver);
        } else { // has parents, let's look it up

            List parents = ConfigurationUtil.buildParentsFromString(configuration, parent);

            if (parents.size() <= 0) {
                LOG.error("Unable to find parent packages " + parent);

                cfg = new PackageConfig(name, namespace, isAbstract, erResolver);
            } else {
                cfg = new PackageConfig(name, namespace, isAbstract, erResolver, parents);
            }
        }
        
        if (cfg != null) {
            cfg.setLocation(DomHelper.getLocationObject(packageElement));
        }
        return cfg;
    }

    /**
     * Build a map of ResultConfig objects from below a given XML element.
     */
    protected Map buildResults(Element element, PackageConfig packageContext) {
        NodeList resultEls = element.getElementsByTagName("result");

        Map results = new LinkedHashMap();

        for (int i = 0; i < resultEls.getLength(); i++) {
            Element resultElement = (Element) resultEls.item(i);

            if (resultElement.getParentNode().equals(element) || resultElement.getParentNode().getNodeName().equals(element.getNodeName())) {
                String resultName = resultElement.getAttribute("name");
                String resultType = resultElement.getAttribute("type");

                // if you don't specify a name on <result/>, it defaults to "success"
                if (!TextUtils.stringSet(resultName)) {
                    resultName = Action.SUCCESS;
                }

                // there is no result type, so let's inherit from the parent package
                if (!TextUtils.stringSet(resultType)) {
                    resultType = packageContext.getFullDefaultResultType();

                    // now check if there is a result type now
                    if (!TextUtils.stringSet(resultType)) {
                        // uh-oh, we have a problem
                        LOG.error("No result type specified for result named '" + resultName + "', perhaps the parent package does not specify the result type?");
                    }
                }


                ResultTypeConfig config = (ResultTypeConfig) packageContext.getAllResultTypeConfigs().get(resultType);

                if (config == null) {
                    throw new ConfigurationException("There is no result type defined for type '" + resultType + "' mapped with name '" + resultName + "'", resultElement);
                }

                String resultClass = config.getClazz();

                // invalid result type specified in result definition
                if (resultClass == null) {
                    LOG.error("Result type '" + resultType + "' is invalid. Modify your xwork.xml file.");
                }

                Map resultParams = XmlHelper.getParams(resultElement);

                if (resultParams.size() == 0) // maybe we just have a body - therefore a default parameter
                {
                    // if <result ...>something</result> then we add a parameter of 'something' as this is the most used result param
                    if ((resultElement.getChildNodes().getLength() >= 1) && (resultElement.getChildNodes().item(0).getNodeType() == Node.TEXT_NODE))
                    {
                        resultParams = new LinkedHashMap();

                        String paramName = config.getDefaultResultParam();
                        if (paramName != null) {
                            String val = XmlHelper.getContent(resultElement);
                            if (val.length() > 0) {
                                resultParams.put(paramName, val);
                            }
                        }
                        else {
                            LOG.warn("no default parameter defined for result of type "+config.getName());
                        }
                    }
                }

                // create new param map, so that the result param can override the config param
                Map params = new LinkedHashMap();
                Map configParams = config.getParams();
                if (configParams != null) {
                    params.putAll(configParams);
                }
                params.putAll(resultParams);

                ResultConfig resultConfig = new ResultConfig(resultName, resultClass, params);
                resultConfig.setLocation(DomHelper.getLocationObject(element));
                results.put(resultConfig.getName(), resultConfig);
            }
        }

        return results;
    }

    /**
     * Build a map of ResultConfig objects from below a given XML element.
     */
    protected List buildExceptionMappings(Element element, PackageConfig packageContext) {
        NodeList exceptionMappingEls = element.getElementsByTagName("exception-mapping");

        List exceptionMappings = new ArrayList();

        for (int i = 0; i < exceptionMappingEls.getLength(); i++) {
            Element ehElement = (Element) exceptionMappingEls.item(i);

            if (ehElement.getParentNode().equals(element) || ehElement.getParentNode().getNodeName().equals(element.getNodeName())) {
                String emName = ehElement.getAttribute("name");
                String exceptionClassName = ehElement.getAttribute("exception");
                String exceptionResult = ehElement.getAttribute("result");

                Map params = XmlHelper.getParams(ehElement);

                if (!TextUtils.stringSet(emName)) {
                    emName = exceptionResult;
                }

                ExceptionMappingConfig ehConfig = new ExceptionMappingConfig(emName, exceptionClassName, exceptionResult, params);
                ehConfig.setLocation(DomHelper.getLocationObject(ehElement));
                exceptionMappings.add(ehConfig);
            }
        }

        return exceptionMappings;
    }


    protected void loadDefaultInterceptorRef(PackageConfig packageContext, Element element) {
        NodeList resultTypeList = element.getElementsByTagName("default-interceptor-ref");

        if (resultTypeList.getLength() > 0) {
            Element defaultRefElement = (Element) resultTypeList.item(0);
            packageContext.setDefaultInterceptorRef(defaultRefElement.getAttribute("name"));
        }
    }
    
    protected void loadDefaultActionRef(PackageConfig packageContext, Element element) {
        NodeList resultTypeList = element.getElementsByTagName("default-action-ref");

        if (resultTypeList.getLength() > 0) {
            Element defaultRefElement = (Element) resultTypeList.item(0);
            packageContext.setDefaultActionRef(defaultRefElement.getAttribute("name"));
        }
    }

    /**
     * Load all of the global results for this package from the XML element.
     */
    protected void loadGlobalResults(PackageConfig packageContext, Element packageElement) {
        NodeList globalResultList = packageElement.getElementsByTagName("global-results");

        if (globalResultList.getLength() > 0) {
            Element globalResultElement = (Element) globalResultList.item(0);
            Map results = buildResults(globalResultElement, packageContext);
            packageContext.addGlobalResultConfigs(results);
        }
    }

    /**
     * Load all of the global results for this package from the XML element.
     */
    protected void loadGobalExceptionMappings(PackageConfig packageContext, Element packageElement) {
        NodeList globalExceptionMappingList = packageElement.getElementsByTagName("global-exception-mappings");

        if (globalExceptionMappingList.getLength() > 0) {
            Element globalExceptionMappingElement = (Element) globalExceptionMappingList.item(0);
            List exceptionMappings = buildExceptionMappings(globalExceptionMappingElement, packageContext);
            packageContext.addGlobalExceptionMappingConfigs(exceptionMappings);
        }
    }

    //    protected void loadIncludes(Element rootElement, DocumentBuilder db) throws Exception {
    //        NodeList includeList = rootElement.getElementsByTagName("include");
    //
    //        for (int i = 0; i < includeList.getLength(); i++) {
    //            Element includeElement = (Element) includeList.item(i);
    //            String fileName = includeElement.getAttribute("file");
    //            includedFileNames.add(fileName);
    //            loadConfigurationFile(fileName, db);
    //        }
    //    }
    protected InterceptorStackConfig loadInterceptorStack(Element element, PackageConfig context) throws ConfigurationException {
        String name = element.getAttribute("name");

        InterceptorStackConfig config = new InterceptorStackConfig(name);
        config.setLocation(DomHelper.getLocationObject(element));
        NodeList interceptorRefList = element.getElementsByTagName("interceptor-ref");

        for (int j = 0; j < interceptorRefList.getLength(); j++) {
            Element interceptorRefElement = (Element) interceptorRefList.item(j);
            List interceptors = lookupInterceptorReference(context, interceptorRefElement);
            config.addInterceptors(interceptors);
        }

        return config;
    }

    protected void loadInterceptorStacks(Element element, PackageConfig context) throws ConfigurationException {
        NodeList interceptorStackList = element.getElementsByTagName("interceptor-stack");

        for (int i = 0; i < interceptorStackList.getLength(); i++) {
            Element interceptorStackElement = (Element) interceptorStackList.item(i);

            InterceptorStackConfig config = loadInterceptorStack(interceptorStackElement, context);

            context.addInterceptorStackConfig(config);
        }
    }

    protected void loadInterceptors(PackageConfig context, Element element) throws ConfigurationException {
        NodeList interceptorList = element.getElementsByTagName("interceptor");

        for (int i = 0; i < interceptorList.getLength(); i++) {
            Element interceptorElement = (Element) interceptorList.item(i);
            String name = interceptorElement.getAttribute("name");
            String className = interceptorElement.getAttribute("class");

            Map params = XmlHelper.getParams(interceptorElement);
            InterceptorConfig config = new InterceptorConfig(name, className, params);
            config.setLocation(DomHelper.getLocationObject(interceptorElement));

            context.addInterceptorConfig(config);
        }

        loadInterceptorStacks(element, context);
    }

    //    protected void loadPackages(Element rootElement) throws ConfigurationException {
    //        NodeList packageList = rootElement.getElementsByTagName("package");
    //
    //        for (int i = 0; i < packageList.getLength(); i++) {
    //            Element packageElement = (Element) packageList.item(i);
    //            addPackage(packageElement);
    //        }
    //    }
    private void loadConfigurationFile(String fileName, Element includeElement) {
        if (!includedFileNames.contains(fileName)) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Loading xwork configuration from: " + fileName);
            }

            includedFileNames.add(fileName);
            List packages=new ArrayList();
            if(configFilePackages.containsKey(fileName)){
            	packages=(List) configFilePackages.get(fileName);
            }

            Document doc = null;
            InputStream is = null;

            try {
                is = getInputStream(fileName);

                if (is == null) {
                    throw new Exception("Could not open file " + fileName);
                }

                InputSource in = new InputSource(is);
                
                //FIXME: we shouldn't be doing this lookup twice
                in.setSystemId(ClassLoaderUtil.getResource(fileName, getClass()).toString());
                
                Map dtdMappings = new HashMap();
                dtdMappings.put("-//OpenSymphony Group//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
                dtdMappings.put("-//OpenSymphony Group//XWork 1.1//EN", "xwork-1.1.dtd");
                dtdMappings.put("-//OpenSymphony Group//XWork 1.0//EN", "xwork-1.0.dtd");
                
                doc = DomHelper.parse(in, dtdMappings);
            } catch (XworkException e) {
                if (includeElement != null) {
                    throw new ConfigurationException(e, includeElement);
                } else {
                    throw new ConfigurationException(e);
                }
            } catch (Exception e) {
                final String s = "Caught exception while loading file " + fileName;
                LOG.error(s, e);
                throw new ConfigurationException(s, e, includeElement);
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        LOG.error("Unable to close input stream", e);
                    }
                }
            }

            Element rootElement = doc.getDocumentElement();
            NodeList children = rootElement.getChildNodes();
            int childSize = children.getLength();

            for (int i = 0; i < childSize; i++) {
                Node childNode = children.item(i);

                if (childNode instanceof Element) {
                    Element child = (Element) childNode;

                    final String nodeName = child.getNodeName();

                    if (nodeName.equals("package")) {
                        addPackage(child);
                        packages.add(TextUtils.noNull(child.getAttribute("name")));
                    } else if (nodeName.equals("include")) {
                        String includeFileName = child.getAttribute("file");
                        loadConfigurationFile(includeFileName, child);
                    }
                }
            }
            configFilePackages.put(fileName, packages);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Loaded xwork configuration from: " + fileName);
            }
        }
    }

    /**
     * Looks up the Interceptor Class from the interceptor-ref name and creates an instance, which is added to the
     * provided List, or, if this is a ref to a stack, it adds the Interceptor instances from the List to this stack.
     *
     * @param interceptorRefElement Element to pull interceptor ref data from
     * @param context               The PackageConfig to lookup the interceptor from
     * @return A list of Interceptor objects
     */
    private List lookupInterceptorReference(PackageConfig context, Element interceptorRefElement) throws ConfigurationException {
        String refName = interceptorRefElement.getAttribute("name");
        Map refParams = XmlHelper.getParams(interceptorRefElement);
        return InterceptorBuilder.constructInterceptorReference(context, refName, refParams);
    }
    
    private void initExtensions(){
    	ServiceReference serviceRef=WebworkActivator.getContext().getServiceReference(IExtensionRegistry.class.getName());
    	IExtensionRegistry service=(IExtensionRegistry) WebworkActivator.getContext().getService(serviceRef);
    	IExtension[] extensions=service.getExtensionPoint(NAMESPACE+"."+EXTENSION_POINT).getExtensions();
    	for (int i = 0; i < extensions.length; i++) {
			IConfigurationElement[] elements=extensions[i].getConfigurationElements();
			for (int j = 0; j < elements.length; j++) {
				loadConfigurationFile(elements[j].getAttribute("configFile"), null);
			}
		}
    	service.addRegistryChangeListener(this, NAMESPACE);
    }
    
}
